package pfc.pfcjoc.entities;

import static playn.core.PlayN.log;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;

import pfc.pfcjoc.core.pfcjoc;
import pfc.pfcjoc.core.sprites.Sprite;

import playn.core.GroupLayer;
import playn.core.Layer;
import playn.core.PlayN;
import playn.core.ResourceCallback;
import playn.core.Sound;

public class Player extends PhysicElement {
  public static String name = "player";
  private int timeSinceLastSprite = 0;

  public static final float MAX_SPEED = 5;

  private int numSpritesRunAnim = 2;

  private boolean dead = false;

  private boolean controlUp, controlDown, controlLeft, controlRight = false;
  public int numFootContacts = 0;
  public int numFootContactsOnRamp = 0;
  public int numFootContactsOnEnemy = 0;
  public int numBodyContactsOnEnemy = 0;
  public int numFootContactsOnPlatform = 0;
  private WoodBlock movingPlatform = null;

  private Sound jumpSound;


  public Player(final World world, final GroupLayer worldLayer, final Vec2 pos) {
    super(world, worldLayer, pos);
    setDepth(200);
    initPhysics(world, pos);
    jumpSound = PlayN.assets().getSound("audio/jump");
    jumpSound.addCallback(new ResourceCallback<Sound>() {
      @Override
      public void done(Sound sound) {
        log().info("Sound loaded!");
      }


      @Override
      public void error(Throwable err) {
        log().error("Error loading sound!", err);
      }
    });
  }


  public void initPhysics(World world, Vec2 pos) {
    position = pfcjoc.toPhy(pos);
    BodyDef bodyDef = new BodyDef();
    bodyDef.type = BodyType.DYNAMIC;
    bodyDef.position = pfcjoc.toPhy(pos);
    bodyDef.fixedRotation = true;
    bodyDef.linearDamping = 0.0f;
    body = world.createBody(bodyDef);

    // PolygonShape boxShape = new PolygonShape();
    // boxShape.setAsBox(w / 2f, h / 2f);

    float w = pfcjoc.toPhy(getWidth());
    float h = pfcjoc.toPhy(getHeight());

    CircleShape boxShape = new CircleShape();
    boxShape.m_radius = w / 2f;

    FixtureDef fxtDefPlayerBody = new FixtureDef();
    fxtDefPlayerBody.shape = boxShape;
    fxtDefPlayerBody.density = 1.5f;
    fxtDefPlayerBody.friction = 1.0f;
    fxtDefPlayerBody.restitution = 0.0f;
    Fixture playerBodyFxtFixture = body.createFixture(fxtDefPlayerBody);
    playerBodyFxtFixture.setUserData("playerBody");
    body.setLinearDamping(0.1f);

    // Sensor als peus per saber quan es pot saltar i altres coses
    PolygonShape sensorPeus = new PolygonShape();
    sensorPeus.setAsBox(w / 2.1f, 0.005f, new Vec2(0, h / 2f), 0);

    FixtureDef fxtDefSensorPeus = new FixtureDef();
    fxtDefSensorPeus.isSensor = true;
    fxtDefSensorPeus.shape = sensorPeus;

    Fixture sensorPeusFxt = body.createFixture(fxtDefSensorPeus);
    sensorPeusFxt.setUserData("sensorPeus");

    // bandes envoltant player per no enganxar-se a les parets
    PolygonShape limitDretShape = new PolygonShape();
    limitDretShape.setAsBox(0.005f, h / 2f - h / 10 / 2f, new Vec2(0 + w / 2f + 0.005f / 2, 0), 0);

    FixtureDef fixtureDefDreta = new FixtureDef();
    fixtureDefDreta.friction = 0.0f;
    fixtureDefDreta.shape = limitDretShape;

    body.createFixture(fixtureDefDreta);

    PolygonShape limitEsqShape = new PolygonShape();
    limitEsqShape.setAsBox(0.005f, h / 2f - h / 10 / 2f, new Vec2(0 - w / 2f + 0.005f / 2, 0), 0);

    FixtureDef fixtureDefEsq = new FixtureDef();
    fixtureDefEsq.friction = 0.0f;
    fixtureDefEsq.shape = limitEsqShape;

    body.createFixture(fixtureDefEsq);
    // log().info("Massa de player: " + String.valueOf(body.getMass()));

    // init prevX, prexY, prevA
    prevX = body.getPosition().x;
    prevY = body.getPosition().y;
    prevA = body.getAngle();

    // Útil per gestió de colisions
    body.setUserData(this);
  }


  public void remove() {
    body.getWorld().destroyBody(body);
  }


  public void update(float delta) {
    super.update(delta);

    // player mort?
    if (numBodyContactsOnEnemy > 0) {
      numBodyContactsOnEnemy = 0;
      dead = true;
    }

    // Si V petita parem al jugador. Millor augmentar friction?
    if (Math.abs(body.getLinearVelocity().x) < 2) {
      body.setLinearVelocity(new Vec2(0, body.getLinearVelocity().y));
    }

    if (controlUp) {
      if (numFootContacts > 0) {
        // body.applyForce(new Vec2(0, -400), new Vec2(body.getPosition()));
        body.applyLinearImpulse(new Vec2(0, body.getMass() * -5), body.getPosition());
        if (jumpSound.isPlaying()) {
          jumpSound.stop();
        }
        if (!jumpSound.play()) {
          log().info("Error al reproduir el so jump.mp3");
        }
        // body.setLinearVelocity(new Vec2(body.getLinearVelocity().x, body.getMass() * -10));
      }
    }
    if (controlRight) {
      // PLAYER SOBRE UNA RAMPA
      if (numFootContactsOnRamp > 0) {
        // TODO: calcular vector direccio per aplicar l'impuls
        body.setLinearVelocity(new Vec2(MAX_SPEED / 3f, -MAX_SPEED / 3f));
      }
      // PLAYER A L'AIRE
      // TODO: fer applyForce per contrarrestar inercia
      else if (numFootContacts <= 0) {
        if (body.getLinearVelocity().x < MAX_SPEED) {
          body.applyForce(new Vec2(body.getMass() * 60, 0), new Vec2(body.getPosition()));
        }
        // body.setLinearVelocity(new Vec2(MAX_SPEED / 1.5f, body.getLinearVelocity().y));
      }
      // PLAYER A TERRA
      else {
        // if (body.getLinearVelocity().x < MAX_SPEED) {
        body.setLinearVelocity(new Vec2(MAX_SPEED / 1.2f, body.getLinearVelocity().y));
        // body.applyLinearImpulse(new Vec2(body.getMass() * 2, 0), body.getPosition());
        // }
      }

    }
    if (controlLeft) {
      // PLAYER SOBRE UNA RAMPA
      if (numFootContactsOnRamp > 0) {
        body.setLinearVelocity(new Vec2(-MAX_SPEED / 3f, MAX_SPEED / 3f));
      }
      // PLAYER A L'AIRE
      else if (numFootContacts <= 0) {
        if (body.getLinearVelocity().x > -MAX_SPEED) {
          body.applyForce(new Vec2(body.getMass() * -60, 0), new Vec2(body.getPosition()));
        }
        // body.setLinearVelocity(new Vec2(-MAX_SPEED / 1.5f, body.getLinearVelocity().y));
      }
      // PLAYER A TERRA
      else {
        // if (body.getLinearVelocity().x > -MAX_SPEED) {
        body.setLinearVelocity(new Vec2(-MAX_SPEED / 1.2f, body.getLinearVelocity().y));
        // body.applyLinearImpulse(new Vec2(body.getMass() * -2, 0), body.getPosition());
        // }
      }
    }

    // sprites corresponents segons toqui

    // MIRROR IMAGE? -> codi afegit a Sprite.java
    if (body.getLinearVelocity().x > 0) {
      sprite.unSetHoritzontalMirrored();
    }
    else if (body.getLinearVelocity().x < 0) {
      sprite.setHoritzontalMirrored();
    }
    // STANDING
    else {
      spriteIndex = 1;
      sprite.setSprite("sprite_0");

    }
    // JUMPING
    if (numFootContacts < 1) {
      spriteIndex = 1;
      sprite.setSprite("jumping");
    }
    // RUNNING
    else if (Math.abs(body.getLinearVelocity().x) > 0 && movingPlatform == null) {
      if (timeSinceLastSprite > 2) {
        timeSinceLastSprite = 0;
        spriteIndex = (spriteIndex + 1) % numSpritesRunAnim;
        sprite.setSprite(spriteIndex);
      }
      timeSinceLastSprite += 1;
    }
    else if (Math.abs(body.getLinearVelocity().x) > WoodBlock.MAX_SPEED && movingPlatform != null) {
      if (timeSinceLastSprite > 2) {
        timeSinceLastSprite = 0;
        spriteIndex = (spriteIndex + 1) % numSpritesRunAnim;
        sprite.setSprite(spriteIndex);
      }
      timeSinceLastSprite += 1;
    }

    if (numFootContactsOnEnemy > 0) {
      setRebound();
    }

    // TODO: Player a plataforma movil
    // if (numFootContactsOnPlatform > 0) {
    if (movingPlatform != null) {
      log().info("PLATFORM ACC");
      // sprite.setSprite("sprite_0");
      if (movingPlatform.isMovingRight()) {
        if (body.getLinearVelocity().x < WoodBlock.MAX_SPEED) {
          body.setLinearVelocity(new Vec2(body.getLinearVelocity().x + WoodBlock.MAX_SPEED, body.getLinearVelocity().y));
        }
      }
      else {
        if (body.getLinearVelocity().x > -WoodBlock.MAX_SPEED) {
          body.setLinearVelocity(new Vec2(body.getLinearVelocity().x - WoodBlock.MAX_SPEED, body.getLinearVelocity().y));
        }
      }
    }
  }


  public void setMovingPlatform(WoodBlock platform) {
    this.movingPlatform = platform;
  }


  public void setControlUp(boolean pressed) {
    this.controlUp = pressed;
  }


  public void setControlDown(boolean pressed) {
    this.controlDown = pressed;
  }


  public void setControlLeft(boolean pressed) {
    this.controlLeft = pressed;
  }


  public void setControlRight(boolean pressed) {
    this.controlRight = pressed;
  }


  public Body getBody() {
    return body;
  }


  public String getName() {
    return name;
  }


  @Override
  public float getWidth() {
    return 32;
  }


  @Override
  public float getHeight() {
    return 32;
  }


  @Override
  protected int getFirstSprite() {
    return 0;
  }


  @Override
  protected String getImagePath() {
    return "images/mario3.png";
  }


  @Override
  protected String getJsonPath() {
    return "images/mario3.json";
  }


  // public Vec2 getPosition() {
  // return position;
  // }

  public Vec2 getScreenPosition() {
    pythagoras.f.Point p = Layer.Util.layerToScreen(sprite.layer(), sprite.layer().originX(), sprite.layer().originY());
    return new Vec2(p.x, p.y);
  }


  public void setRebound() {
    // log().info("REBOTE");
    body.applyLinearImpulse(new Vec2(0, body.getMass() * -5), body.getPosition());
  }


  public boolean isDead() {
    return dead;
  }


  public void revive(Vec2 playerStartPos) {
    this.setPosPhy(pfcjoc.toPhy(playerStartPos));
    dead = false;
  }
}
